/*
 * mod_user_pap2_voice_config.c
 *
 *  Created on: Mar 4, 2012
 *      Author: Administrator
 */

#include <stdio.h>
#include <string.h>
#include "user.h"
#include "utils.h"
#include "micro_libxml.h"
#include "xmppconnect.h"

struct my_struct_stat
{
	char pad1[48];
	int size;
	char pad2[4];
	int ctime;
	char pad3[4];
	int mtime;
	char pad4[4];
	char pad5[128];
};

static struct _sip_user_ *sip_users = NULL;
static struct my_struct_stat configfile_stat;
static int xmpp_2_sip_mod_user_pap2_voice_config_find_user_by_sip_user_name(char *sip_username,struct _sip_user_ **user);
static int load_sip_users();
static void free_sip_users();
static struct user_manager xmpp_2_sip_user_manager = {
		.find_user_by_sip_user_name = xmpp_2_sip_mod_user_pap2_voice_config_find_user_by_sip_user_name,
};
static char mod_user_pap2_voice_stopping = 0;
static pthread_t mod_user_pap2_voice_detect_config_thread = 0;
static enum MICRO_LIBXML_FOUND_ELEMENT_RETURN provision_found_element(micro_libxml_parser_t *parser,void *userdata,micro_libxml_xmpp_element_t *element)
{

	if(!xmpp_cmpstr(element->element_name,"PORT_2_CONFIG",element->element_name_len) || !xmpp_cmpstr(element->element_name,"PORT_1_CONFIG",element->element_name_len))
	{
		char google_voice_enabled = 0;
		char google_voice_username[255],google_voice_password[255],sip_username[255],sip_password[255];
		micro_libxml_xmpp_element_t *tmp = element->childs;
		LogD("find PORT_X_CONFIG");
		while(tmp)
		{
			if(!xmpp_cmpstr(tmp->element_name,"GOOGLEVOICE_STATUS",tmp->element_name_len) && !xmpp_cmpstr(tmp->element_value,"1",tmp->element_value_len))
			{
				google_voice_enabled = 1;
			}
			else if(!xmpp_cmpstr(tmp->element_name,"GOOGLE_VOICE_AUTH_USER_NAME",tmp->element_name_len))
			{
				snprintf(google_voice_username,255,"%.*s",tmp->element_value_len,tmp->element_value);
			}
			else if(!xmpp_cmpstr(tmp->element_name,"GOOGLE_VOICE_AUTH_USER_PASSWD",tmp->element_name_len))
			{
				snprintf(google_voice_password,255,"%.*s",tmp->element_value_len,tmp->element_value);
			}
			else if(!xmpp_cmpstr(tmp->element_name,"AUTH_USER_NAME",tmp->element_name_len))
			{
				snprintf(sip_username,255,"%.*s",tmp->element_value_len,tmp->element_value);
			}
			else if(!xmpp_cmpstr(tmp->element_name,"AUTH_USER_PASSWD",tmp->element_name_len))
			{
				snprintf(sip_password,255,"%.*s",tmp->element_value_len,tmp->element_value);
			}
			tmp = tmp->next;
		}
		if(google_voice_enabled)
		{
			char renewuser = 0;
			xmpp_connection_t *xmpp_connection = NULL;
			struct _sip_user_ *user = NULL;
			if(xmpp_2_sip_find_user_by_sip_user_name(sip_username,&user) || !user)
			{
				user = sipxmpp_calloc(sizeof(struct _sip_user_));
				renewuser = 0;
			}
			else
				renewuser = 1;
			strcpy(user->sip_username,sip_username);
			strcpy(user->sip_password,sip_password);
			strcpy(user->xmpp_username,google_voice_username);
			strcpy(user->xmpp_password,google_voice_password);

			if(!sipxmpp_find_connection_by_sip_username(user->xmpp_username,&xmpp_connection) && xmpp_connection)
			{
				strncpy(xmpp_connection->username,user->xmpp_username,128);
				strncpy(xmpp_connection->password,user->xmpp_password,128);
				if(xmpp_connection->status == SIPXMPP_CONNECTION_STATUS_ERROR)
					xmpp_connection->status = SIPXMPP_CONNECTION_STATUS_DISCONNECTED;
			}
			user->delete_flag = 1;
			if(!renewuser)
			{
				if(!sip_users)
				{
					sip_users = user;
				}
				else
				{
					struct _sip_user_ *tmp_user = sip_users;
					while(tmp_user->next)
					{
						tmp_user = tmp_user->next;
					}
					tmp_user->next = user;
				}
			}

		}
		LogD("return FOUND_ELEMENT_RETURN_FREE_CURRENT_ELEMENT");
		return MICRO_LIBXML_FOUND_ELEMENT_RETURN_FREE_CURRENT_ELEMENT;
	}
	else if(!xmpp_cmpstr(element->element_name,"VOICE_XML",element->element_name_len))
	{
		LogD("find VOICE_XML");
		*(parser->stopping) = 1;
		return MICRO_LIBXML_FOUND_ELEMENT_RETURN_FREE_ROOT;
	}

	else if(!xmpp_cmpstr(element->element_name,"COMMON_CODING_CONFIG",element->element_name_len) || !xmpp_cmpstr(element->element_name,"SIP_CONFIG",element->element_name_len))
	{
		LogD("found COMMON_CODING_CONFIG");
		return MICRO_LIBXML_FOUND_ELEMENT_RETURN_FREE_CURRENT_ELEMENT;
	}
	return MICRO_LIBXML_FOUND_ELEMENT_RETURN_FREE_NOTHING;
}
static int provision_read(micro_libxml_parser_t *parser,void *userdata)
{
	return 0;
}
static int load_sip_users()
{

	FILE *provision_fp;
	int retcode;
	char parser_stopping = 0;
	size_t readbuffer_len;
	provision_fp = fopen("/var/tmp/voice_config","rb");
	if(provision_fp == NULL)
	{
		LogE("/var/tmp/voice_config not found. are you sure you are running on pap2v2?");
		gotofinish(1);
	}
	fseek (provision_fp, 0, SEEK_END);
	readbuffer_len = ftell(provision_fp);
	fseek ( provision_fp , 0 , SEEK_SET );

	micro_libxml_parser_t parser = {
		.stopping = &parser_stopping,
		.readbuffer = sipxmpp_calloc(readbuffer_len),
		.readbuffer_len = readbuffer_len,
		.readbuffer_available_len = 0,
		.read = &provision_read,
		.found = &provision_found_element
	};

	if(!parser.readbuffer)
	{
		LogE("not enough memory to load /var/tmp/voice_config");
		gotofinish(1);
	}

	while(parser.readbuffer_available_len < parser.readbuffer_len)
	{
		LogD("read from config file.");
		parser.readbuffer_available_len += fread(parser.readbuffer + parser.readbuffer_available_len,1,parser.readbuffer_len - parser.readbuffer_available_len,provision_fp);
	}
	micro_libxml_parse_stream(&parser,NULL);
	chkndfree(parser.readbuffer);
	gotofinish(0);
	finish:
		if(provision_fp)
		{
			fclose(provision_fp);
			provision_fp = NULL;
		}
		remove_deleted_users(&sip_users);
		return retcode;
}
static void free_sip_users()
{
	struct _sip_user_ *tmp_user;
	LogD("begin free sip_users");
	while(sip_users)
	{
		LogD("freeing sip_users");
		tmp_user = sip_users->next;
		chkndfree(sip_users);
		sip_users = tmp_user;
		LogD("freeed sip_users");
	}
}
void *detect_config_change(void *obj)
{
	struct my_struct_stat configfile_stat_tmp;
	while(!mod_user_pap2_voice_stopping)
	{
		sleep(10);
		memset(&configfile_stat_tmp,0,sizeof(struct my_struct_stat));

		if(stat("/var/tmp/voice_config",(struct stat *)&configfile_stat_tmp))
		{
			LogE("stat /var/tmp/voice_config error.");
			continue;
		}
		//is changed?
		if(configfile_stat_tmp.size != configfile_stat.size || configfile_stat_tmp.mtime > configfile_stat.mtime) //file changed.
		{
			LogI("config file changed.old size[%d],newsize[%d],old time[%d],newtime[%d]",configfile_stat_tmp.size,configfile_stat.size,configfile_stat_tmp.mtime,configfile_stat.mtime);
			load_sip_users();
		}
		memcpy(&configfile_stat,&configfile_stat_tmp,sizeof(struct my_struct_stat));
	}
	return 0;
}
int mod_init(){
	mod_user_pap2_voice_stopping = 0;
	xmpp_2_sip_set_user_manager(&xmpp_2_sip_user_manager);
	memset(&configfile_stat,0,sizeof(struct my_struct_stat));
	if(stat("/var/tmp/voice_config",(struct stat*)&configfile_stat))
	{
		LogE("stat /var/tmp/voice_config error. stop loading.");
	}
	else
	{
		load_sip_users();
	}

	if(pthread_create(&mod_user_pap2_voice_detect_config_thread,NULL,detect_config_change,NULL))
	{
		Log("create mod_user_pap2_voice_detect_config_thread error");
	}
	return 0;
}
int mod_reload(){
	load_sip_users();
	return 0;
}
int mod_unload(){
	mod_user_pap2_voice_stopping = 1;
	pthread_join(mod_user_pap2_voice_detect_config_thread,NULL);
	mod_user_pap2_voice_detect_config_thread = 0;
	free_sip_users();
	return 0;
}
static int xmpp_2_sip_mod_user_pap2_voice_config_find_user_by_sip_user_name(char *sip_username,struct _sip_user_ **user)
{
	struct _sip_user_ *tmp_user = sip_users;
	while(tmp_user != NULL)
	{
		if(!strncmp(sip_username,tmp_user->sip_username,SIP_USERNAME_LEN))
		{
			if(user != NULL) *user = tmp_user;
			return S_OK;
		}
		tmp_user = tmp_user->next;
	}
	return S_ERROR;
}

